﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DataTransformationServices.Design;

namespace MouldingAndMillwork.SSIS
{
    #region CLASS: BeforeMappingEventArgs
    public class BeforeMappingEventArgs : EventArgs
    {
        #region Private Variables
        bool _cancel;
        ColumnInformation _sourceColumn;
        ColumnInformation _destColumn;
        #endregion

        #region Constructor
        public BeforeMappingEventArgs(ColumnInformation sourceColumn, ColumnInformation destColumn)
        {
            this._cancel = false;
            this._sourceColumn = sourceColumn;
            this._destColumn = destColumn;
        }
        #endregion

        #region Public Properties
        public bool Cancel
        {
            set { this._cancel = value; }
        }

        public bool Cancelled
        {
            get { return this._cancel; }
        }

        public ColumnInformation SourceColumn
        {
            get { return this._sourceColumn; }
        }

        public ColumnInformation DestColumn
        {
            get { return this._destColumn; }
        }
        #endregion
    }
    #endregion

    #region CLASS: AfterMappingEventArgs
    public class AfterMappingEventArgs : EventArgs
    {
        #region Private Variables
        ColumnInformation _sourceColumn;
        ColumnInformation _destColumn;
        #endregion

        #region Constructor
        public AfterMappingEventArgs(ColumnInformation sourceColumn, ColumnInformation destColumn)
        {
            this._sourceColumn = sourceColumn;
            this._destColumn = destColumn;
        }
        #endregion

        #region Public Properties
        public ColumnInformation SourceColumn
        {
            get { return this._sourceColumn; }
        }

        public ColumnInformation DestColumn
        {
            get { return this._destColumn; }
        }
        #endregion
    }
    #endregion

    #region CLASS: DtsMappingSurfaceControlWrapper
    public class DtsMappingSurfaceControlWrapper
    {
        #region Event Handling Delegate/Event definitions
        public delegate void BeforeMappingEventHandler(object sender, ref BeforeMappingEventArgs e);
        public delegate void AfterMappingEventHandler(object sender, AfterMappingEventArgs e);
        public event BeforeMappingEventHandler BeforeMappingCreated;
        public event BeforeMappingEventHandler BeforeMappingDeleted;
        public event AfterMappingEventHandler AfterMappingCreated;
        public event AfterMappingEventHandler AfterMappingDeleted;
        #endregion

        #region Private Variables
        private DtsMappingSurfaceControl _controlMappingSurface;
        private ToolTip _controlToolTip;
        private Timer _controlTimer;
        private ColumnInformationList _firstTableColumns;
        private ColumnInformationList _secondTableColumns;

        private Point _dragStartedAt;
        private Control _dragStartedFrom;
        private Control _toolTipOverControl;
        private long _toolTipRowIndex;
        private string _toolTipTitle;
        private string _toolTipContents;

        private bool _eventsSuspended;
        private List<Microsoft.DataWarehouse.Controls.SurfaceConnection> _selectedConnections;
        #endregion

        #region Constructor
        public DtsMappingSurfaceControlWrapper(DtsMappingSurfaceControl mappingSurfaceControl, ToolTip toolTipControl,
            string firstTableName, ColumnInformationList firstTableColumns, 
            string secondTableName, ColumnInformationList secondTableColumns, 
            MappingList connections)
        {
            this._controlMappingSurface = mappingSurfaceControl;
            this._controlToolTip = toolTipControl;
            this._firstTableColumns = firstTableColumns;
            this._secondTableColumns = secondTableColumns;

            this._controlToolTip.UseFading = true;
            this._controlToolTip.IsBalloon = false;
            this._controlToolTip.ToolTipIcon = ToolTipIcon.None;

            this._controlTimer = new Timer();
            this._controlTimer.Interval = this._controlToolTip.ReshowDelay;
            this._controlTimer.Tick += new EventHandler(_controlTimer_Tick);
            this._controlTimer.Enabled = true;

            this._controlMappingSurface.MouseLeave += new EventHandler(_controlMappingSurface_MouseLeave);

            ColumnInfo[] firstTableColumnInfos = new ColumnInfo[firstTableColumns.Count];
            for (int index = 0; index < firstTableColumns.Count; index++)
            {
                firstTableColumnInfos[index] = new ColumnInfo();
                firstTableColumnInfos[index].Name = this._firstTableColumns[index].Name;
            }

            ColumnInfo[] secondTableColumnInfos = new ColumnInfo[secondTableColumns.Count];
            for (int index = 0; index < secondTableColumns.Count; index++)
            {
                secondTableColumnInfos[index] = new ColumnInfo();
                secondTableColumnInfos[index].Name = this._secondTableColumns[index].Name;
            }

            this._controlMappingSurface.TwoTables = true;
            this._controlMappingSurface.InitializeFirstTable(firstTableColumnInfos);
            this._controlMappingSurface.InitializeSecondTable(secondTableColumnInfos);
            this._controlMappingSurface.FirstMovableGridControl.Text = firstTableName;
            this._controlMappingSurface.FirstMovableGridControl.GridLayout = Microsoft.DataTransformationServices.Design.MovableSizableGrid.GridLayoutCase.Check_Name;
            this._controlMappingSurface.SecondMovableGridControl.Text = secondTableName;
            this._controlMappingSurface.SecondMovableGridControl.GridLayout = Microsoft.DataTransformationServices.Design.MovableSizableGrid.GridLayoutCase.Check_Name;

            this._controlMappingSurface.SurfaceConnectionRemoved += new Microsoft.DataWarehouse.Controls.SurfaceConnectionRemovedEventHandler(_controlMappingSurface_SurfaceConnectionRemoved);
            this._controlMappingSurface.SurfaceConnectionAdded += new Microsoft.DataWarehouse.Controls.SurfaceConnectionAddedEventHandler(_controlMappingSurface_SurfaceConnectionAdded);
            this._controlMappingSurface.DeleteConnectorsEvent += new DeleteConnectorsEventHandler(_controlMappingSurface_DeleteConnectorsEvent);
            this._controlMappingSurface.SelectedSurfaceConnectionAdded += new Microsoft.DataWarehouse.Controls.SelectedSurfaceConnectionAddedEventHandler(_controlMappingSurface_SelectedSurfaceConnectionAdded);
            this._controlMappingSurface.SelectedSurfaceConnectionRemoved += new Microsoft.DataWarehouse.Controls.SelectedSurfaceConnectionRemovedEventHandler(_controlMappingSurface_SelectedSurfaceConnectionRemoved);

            #region Set up Drag/Drop
            this._controlMappingSurface.FirstMovableGridControl.AllowDrop = true;
            this._controlMappingSurface.SecondMovableGridControl.AllowDrop = true;

            #region Set up first table as drag/drop source
            this._controlMappingSurface.FirstMovableGridControl.GridControl.MouseDown += new MouseEventHandler(FirstMovableGridControl_MouseDown);
            this._controlMappingSurface.FirstMovableGridControl.GridControl.MouseUp += new MouseEventHandler(FirstMovableGridControl_MouseUp);
            this._controlMappingSurface.FirstMovableGridControl.GridControl.MouseMove += new MouseEventHandler(FirstMovableGridControl_MouseMove);
            #endregion

            #region Set up second table as drag/drop source
            this._controlMappingSurface.SecondMovableGridControl.GridControl.MouseDown += new MouseEventHandler(SecondMovableGridControl_MouseDown);
            this._controlMappingSurface.SecondMovableGridControl.GridControl.MouseUp += new MouseEventHandler(SecondMovableGridControl_MouseUp);
            this._controlMappingSurface.SecondMovableGridControl.GridControl.MouseMove += new MouseEventHandler(SecondMovableGridControl_MouseMove);
            #endregion

            #region Set up first table as drag/drop destination
            this._controlMappingSurface.FirstMovableGridControl.DragOver += new DragEventHandler(FirstMovableGridControl_DragOver);
            this._controlMappingSurface.FirstMovableGridControl.DragDrop += new DragEventHandler(FirstMovableGridControl_DragDrop);
            #endregion

            #region Set up second table as drag/drop destination
            this._controlMappingSurface.SecondMovableGridControl.GridControl.DragOver += new DragEventHandler(SecondMovableGridControl_DragOver);
            this._controlMappingSurface.SecondMovableGridControl.GridControl.DragDrop += new DragEventHandler(SecondMovableGridControl_DragDrop);
            #endregion
            #endregion

            #region Map columns
            this._eventsSuspended = true;
            foreach (Mapping mapping in connections)
            {
                try
                {
                    this._controlMappingSurface.AddConnector(mapping.FirstColumnName, mapping.SecondColumnName);
                }
                #region catch ...
                catch (Exception ex)
                {
                    throw new ArgumentException("Unable to map " + mapping.FirstColumnName + " to " + mapping.SecondColumnName + ".", ex);
                }
                #endregion
            }
            this._eventsSuspended = false;
            #endregion

            this._selectedConnections = new List<Microsoft.DataWarehouse.Controls.SurfaceConnection>();
        }
        #endregion

        #region Public Methods to Manipulate Mappings
        public MappingList GetMappingList()
        {
            MappingList mappingList = new MappingList();

            foreach (DtsSurfaceConnection connection in this._controlMappingSurface.Connections)
            {
                ColumnInformation source = this._firstTableColumns[(string)connection.SourceConnectorObject];
                ColumnInformation destination = this._secondTableColumns[(string)connection.DestinationConnectorObject];
                mappingList.AddMapping(source, destination);
            }

            return mappingList;
        }

        public void RemoveAllMappings()
        {
            while (this._controlMappingSurface.Connections.Count > 0)
            {
                BeforeMappingEventArgs beforeMappingEventArgs = new BeforeMappingEventArgs(
                    this._firstTableColumns[(string)this._controlMappingSurface.Connections[0].SourceConnectorObject],
                    this._secondTableColumns[(string)this._controlMappingSurface.Connections[0].DestinationConnectorObject]);
                this.RaiseBeforeMappingDeletedEvent(ref beforeMappingEventArgs);
                if (!beforeMappingEventArgs.Cancelled)
                {
                    this._controlMappingSurface.Connections.RemoveAt(0); // results in the control's event handler being called, in which I call the "after" event
                }
            }
        }

        public void AddMapping(string firstTableColumnName, string secondTableColumnName)
        {
            BeforeMappingEventArgs beforeMappingEventArgs = new BeforeMappingEventArgs(
                this._firstTableColumns[firstTableColumnName],
                this._secondTableColumns[secondTableColumnName]);
            this.RaiseBeforeMappingDeletedEvent(ref beforeMappingEventArgs);
            if (!beforeMappingEventArgs.Cancelled)
            {
                this._controlMappingSurface.AddConnector(firstTableColumnName, secondTableColumnName); // results in the control's event handler being called, in which I call the "after" event
            }
        }
        #endregion

        #region MappingSurfaceControl events
        void _controlMappingSurface_SurfaceConnectionAdded(object sender, Microsoft.DataWarehouse.Controls.SurfaceConnectionEventArgs args)
        {
            AfterMappingEventArgs afterMappingEventArgs = new AfterMappingEventArgs(this._firstTableColumns[(string)args.Connection.SourceConnectorObject], this._secondTableColumns[(string)args.Connection.DestinationConnectorObject]);
            this.RaiseAfterMappingCreatedEvent(afterMappingEventArgs);
        }

        void _controlMappingSurface_SurfaceConnectionRemoved(object sender, Microsoft.DataWarehouse.Controls.SurfaceConnectionEventArgs args)
        {
            AfterMappingEventArgs afterMappingEventArgs = new AfterMappingEventArgs(this._firstTableColumns[(string)args.Connection.SourceConnectorObject], this._secondTableColumns[(string)args.Connection.DestinationConnectorObject]);
            this.RaiseAfterMappingDeletedEvent(afterMappingEventArgs);
        }

        void _controlMappingSurface_SelectedSurfaceConnectionRemoved(object sender, Microsoft.DataWarehouse.Controls.SurfaceConnectionEventArgs args)
        {
            this._selectedConnections.Remove(args.Connection);
        }

        void _controlMappingSurface_SelectedSurfaceConnectionAdded(object sender, Microsoft.DataWarehouse.Controls.SurfaceConnectionEventArgs args)
        {
            this._selectedConnections.Add(args.Connection);
        }

        void _controlMappingSurface_DeleteConnectorsEvent(object sender, DeleteConnectorsEventArgs args)
        {
            // Called when the user has selected some connectors by mouse, then presses the delete key.
            foreach (Microsoft.DataWarehouse.Controls.SurfaceConnection connection in this._selectedConnections)
            {
                BeforeMappingEventArgs beforeMappingEventArgs = new BeforeMappingEventArgs(
                    this._firstTableColumns[(string)connection.SourceConnectorObject],
                    this._secondTableColumns[(string)connection.DestinationConnectorObject]);
                this.RaiseBeforeMappingDeletedEvent(ref beforeMappingEventArgs);
                if (beforeMappingEventArgs.Cancelled)
                {
                    args.CancelAction = true;
                    break;
                }
            }
        }
        #endregion

        #region Drag/Drop and Tooltip event handlers
        #region Timer for "filling" the tooltip
        void _controlMappingSurface_MouseLeave(object sender, EventArgs e)
        {
            this._toolTipOverControl = null;
        }

        private void _controlTimer_Tick(object sender, EventArgs e)
        {
            if (this._toolTipOverControl != null)
            {
                if (this._controlToolTip.Active)
                {
                    if ((this._toolTipTitle != this._controlToolTip.ToolTipTitle) || (this._toolTipContents != this._controlToolTip.GetToolTip(this._toolTipOverControl)))
                    {
                        this._controlToolTip.ToolTipTitle = this._toolTipTitle;
                        this._controlToolTip.SetToolTip(this._toolTipOverControl, this._toolTipContents);
                    }
                }
            }
        }
        #endregion

        #region First Table's Drag/Drop Events
        #region Events that support "sourcing" the drag/drop
        void FirstMovableGridControl_MouseDown(object sender, MouseEventArgs e)
        {
            this._dragStartedAt = e.Location;
        }

        void FirstMovableGridControl_MouseUp(object sender, MouseEventArgs e)
        {
            this._dragStartedAt = Point.Empty;
        }

        void FirstMovableGridControl_MouseMove(object sender, MouseEventArgs e)
        {
            // find the row they're hovering over
            Microsoft.SqlServer.Management.UI.Grid.HitTestInfo hitTestInfo = this._controlMappingSurface.FirstMovableGridControl.GridControl.HitTest(e.X, e.Y);
            if ((hitTestInfo.RowIndex >= 0) && ((hitTestInfo.RowIndex != this._toolTipRowIndex) || (this._toolTipOverControl != (Control)sender)))
            {
                this._toolTipOverControl = (Control)sender;
                this._toolTipRowIndex = hitTestInfo.RowIndex;
                string hoverColumn = this._controlMappingSurface.FirstMovableGridControl.GridControl.GetCellString((int)hitTestInfo.RowIndex, this._controlMappingSurface.FirstMovableGridControl.ColumnNameIndex);
                ColumnInformation columnInfo = this._firstTableColumns[hoverColumn];
                this._toolTipTitle = columnInfo.Name;
                this._toolTipContents = SSISUtility.DataTypeInformation.BuildDescription(columnInfo.DataType, columnInfo.Length, columnInfo.Scale, columnInfo.Precision, columnInfo.CodePage);
            }

            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                Size dragDistance = new Size(Math.Abs(this._dragStartedAt.X - e.X), Math.Abs(this._dragStartedAt.Y - e.Y));
                // If the mouse moves outside the rectangle, start the drag.
                if ((this._dragStartedAt != Point.Empty)
                    && ((dragDistance.Width > (SystemInformation.DragSize.Width / 2)) || (dragDistance.Height > (SystemInformation.DragSize.Height / 2))))
                {
                    hitTestInfo = this._controlMappingSurface.FirstMovableGridControl.GridControl.HitTest(this._dragStartedAt.X, this._dragStartedAt.Y);
                    // Proceed with the drag-and-drop, passing in the list item.                    
                    if (hitTestInfo.RowIndex >= 0)
                    {
                        this._dragStartedFrom = this._controlMappingSurface.FirstMovableGridControl;
                        string sourceDragColumn = this._controlMappingSurface.FirstMovableGridControl.GridControl.GetCellString((int)hitTestInfo.RowIndex, this._controlMappingSurface.FirstMovableGridControl.ColumnNameIndex);
                        DragDropEffects dropEffect = this._controlMappingSurface.FirstMovableGridControl.DoDragDrop(sourceDragColumn, DragDropEffects.Link);
                    }
                }
            }
        }
        #endregion

        #region Events that support "receiving" the drag/drop
        void FirstMovableGridControl_DragOver(object sender, DragEventArgs e)
        {
            if (sender == this._dragStartedFrom)
            {
                e.Effect = DragDropEffects.None;
            }
            else if (!e.Data.GetDataPresent(typeof(System.String)))
            {
                e.Effect = DragDropEffects.None;
            }
            else
            {
                e.Effect = DragDropEffects.Link;
            }
        }

        void FirstMovableGridControl_DragDrop(object sender, DragEventArgs e)
        {
            // Ensure that the list item index is contained in the data.
            if (e.Data.GetDataPresent(typeof(System.String)))
            {
                Object item = (object)e.Data.GetData(typeof(System.String));

                // Perform drag-and-drop, depending upon the effect.
                if (e.Effect == DragDropEffects.Link)
                {
                    Point clientPoint = this._controlMappingSurface.FirstMovableGridControl.PointToClient(new Point(e.X, e.Y));
                    Microsoft.SqlServer.Management.UI.Grid.HitTestInfo hitTestInfo = this._controlMappingSurface.FirstMovableGridControl.GridControl.HitTest(clientPoint.X, clientPoint.Y);
                    if (hitTestInfo.RowIndex >= 0)
                    {
                        string sourceDragColumn = (string)e.Data.GetData(typeof(string));
                        string destDragColumn = this._controlMappingSurface.FirstMovableGridControl.GridControl.GetCellString((int)hitTestInfo.RowIndex, this._controlMappingSurface.FirstMovableGridControl.ColumnNameIndex);

                        BeforeMappingEventArgs beforeMappingEventArgs = new BeforeMappingEventArgs(this._firstTableColumns[destDragColumn], this._secondTableColumns[sourceDragColumn]);
                        
                        foreach (Microsoft.DataTransformationServices.Design.DtsSurfaceConnection connection in this._controlMappingSurface.Connections)
                        {
                            if ((connection.SourceConnectorControl == this._controlMappingSurface.FirstMovableGridControl)
                                && ((string)connection.SourceConnectorObject == sourceDragColumn))
                            {
                                this.RaiseBeforeMappingDeletedEvent(ref beforeMappingEventArgs);
                                if (!beforeMappingEventArgs.Cancelled)
                                {
                                    this._controlMappingSurface.DeleteConnectors((string)connection.SourceConnectorObject); // results in the control's event handler being called, in which I call the "after" event
                                }
                                break;
                            }
                        }
                        if (!beforeMappingEventArgs.Cancelled)
                        {
                            foreach (Microsoft.DataTransformationServices.Design.DtsSurfaceConnection connection in this._controlMappingSurface.Connections)
                            {
                                if ((connection.DestinationConnectorControl == this._controlMappingSurface.FirstMovableGridControl)
                                    && ((string)connection.DestinationConnectorObject == destDragColumn))
                                {
                                    this.RaiseBeforeMappingDeletedEvent(ref beforeMappingEventArgs);
                                    if (!beforeMappingEventArgs.Cancelled)
                                    {
                                        this._controlMappingSurface.DeleteConnectors((string)connection.SourceConnectorObject); // results in the control's event handler being called, in which I call the "after" event
                                    }
                                    break;
                                }
                            }
                        }
                        if (!beforeMappingEventArgs.Cancelled)
                        {
                            this.RaiseBeforeMappingCreatedEvent(ref beforeMappingEventArgs);
                            if (!beforeMappingEventArgs.Cancelled)
                            {
                                this._controlMappingSurface.AddConnector(destDragColumn, sourceDragColumn); // results in the control's event handler being called, in which I call the "after" event
                            }
                        }
                    }
                }
            }
        }
        #endregion
        #endregion

        #region Second Table's Drag/Drop Events
        #region Events that support "sourcing" the drag/drop
        void SecondMovableGridControl_MouseDown(object sender, MouseEventArgs e)
        {
            this._dragStartedAt = new Point(e.X, e.Y);
        }

        void SecondMovableGridControl_MouseUp(object sender, MouseEventArgs e)
        {
            this._dragStartedAt = Point.Empty;
        }

        void SecondMovableGridControl_MouseMove(object sender, MouseEventArgs e)
        {
            // find the row they're hovering over
            Microsoft.SqlServer.Management.UI.Grid.HitTestInfo hitTestInfo = this._controlMappingSurface.SecondMovableGridControl.GridControl.HitTest(e.X, e.Y);
            if ((hitTestInfo.RowIndex >= 0) && ((hitTestInfo.RowIndex != this._toolTipRowIndex) || (this._toolTipOverControl != (Control)sender)))
            {
                this._toolTipOverControl = (Control)sender;
                this._toolTipRowIndex = hitTestInfo.RowIndex;
                string hoverColumn = this._controlMappingSurface.SecondMovableGridControl.GridControl.GetCellString((int)hitTestInfo.RowIndex, this._controlMappingSurface.SecondMovableGridControl.ColumnNameIndex);
                ColumnInformation columnInfo = this._secondTableColumns[hoverColumn];
                this._toolTipTitle = columnInfo.Name;
                this._toolTipContents = SSISUtility.DataTypeInformation.BuildDescription(columnInfo.DataType, columnInfo.Length, columnInfo.Scale, columnInfo.Precision, columnInfo.CodePage);
            }

            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                Size dragDistance = new Size(Math.Abs(this._dragStartedAt.X - e.X), Math.Abs(this._dragStartedAt.Y - e.Y));
                // If the mouse moves outside the rectangle, start the drag.
                if ((this._dragStartedAt != Point.Empty)
                    && ((dragDistance.Width > (SystemInformation.DragSize.Width / 2)) || (dragDistance.Height > (SystemInformation.DragSize.Height / 2))))
                {
                    hitTestInfo = this._controlMappingSurface.SecondMovableGridControl.GridControl.HitTest(this._dragStartedAt.X, this._dragStartedAt.Y);
                    // Proceed with the drag-and-drop, passing in the list item.                    
                    if (hitTestInfo.RowIndex >= 0)
                    {
                        this._dragStartedFrom = this._controlMappingSurface.SecondMovableGridControl.GridControl;
                        string sourceDragColumn = this._controlMappingSurface.SecondMovableGridControl.GridControl.GetCellString((int)hitTestInfo.RowIndex, this._controlMappingSurface.SecondMovableGridControl.ColumnNameIndex);
                        DragDropEffects dropEffect = this._controlMappingSurface.SecondMovableGridControl.DoDragDrop(sourceDragColumn, DragDropEffects.Link);
                    }
                }
            }
        }
        #endregion

        #region Events that support "receiving" the drag/drop
        void SecondMovableGridControl_DragOver(object sender, DragEventArgs e)
        {
            if (sender == this._dragStartedFrom)
            {
                e.Effect = DragDropEffects.None;
            }
            else if (!e.Data.GetDataPresent(typeof(System.String)))
            {
                e.Effect = DragDropEffects.None;
            }
            else
            {
                e.Effect = DragDropEffects.Link;
            }
        }

        void SecondMovableGridControl_DragDrop(object sender, DragEventArgs e)
        {
            // Ensure that the list item index is contained in the data.
            if (e.Data.GetDataPresent(typeof(System.String)))
            {
                Object item = (object)e.Data.GetData(typeof(System.String));

                // Perform drag-and-drop, depending upon the effect.
                if (e.Effect == DragDropEffects.Link)
                {
                    Point clientPoint = this._controlMappingSurface.SecondMovableGridControl.PointToClient(new Point(e.X, e.Y));
                    Microsoft.SqlServer.Management.UI.Grid.HitTestInfo hitTestInfo = this._controlMappingSurface.SecondMovableGridControl.GridControl.HitTest(clientPoint.X, clientPoint.Y);
                    if (hitTestInfo.RowIndex >= 0)
                    {
                        string sourceDragColumn = (string)e.Data.GetData(typeof(string));
                        string destDragColumn = this._controlMappingSurface.SecondMovableGridControl.GridControl.GetCellString((int)hitTestInfo.RowIndex, this._controlMappingSurface.SecondMovableGridControl.ColumnNameIndex);

                        BeforeMappingEventArgs beforeMappingEventArgs = new BeforeMappingEventArgs(this._firstTableColumns[sourceDragColumn], this._secondTableColumns[destDragColumn]);
                        AfterMappingEventArgs afterMappingEventArgs = new AfterMappingEventArgs(this._firstTableColumns[sourceDragColumn], this._secondTableColumns[destDragColumn]);

                        foreach (Microsoft.DataTransformationServices.Design.DtsSurfaceConnection connection in this._controlMappingSurface.Connections)
                        {
                            if ((connection.SourceConnectorControl == this._controlMappingSurface.FirstMovableGridControl)
                                && ((string)connection.SourceConnectorObject == sourceDragColumn))
                            {
                                this.RaiseBeforeMappingDeletedEvent(ref beforeMappingEventArgs);
                                if (!beforeMappingEventArgs.Cancelled)
                                {
                                    this._controlMappingSurface.DeleteConnectors(sourceDragColumn); // results in the control's event handler being called, in which I call the "after" event
                                }
                                break;
                            }
                        }
                        if (!beforeMappingEventArgs.Cancelled)
                        {
                            foreach (Microsoft.DataTransformationServices.Design.DtsSurfaceConnection connection in this._controlMappingSurface.Connections)
                            {
                                if ((connection.DestinationConnectorControl == this._controlMappingSurface.SecondMovableGridControl)
                                    && ((string)connection.DestinationConnectorObject == destDragColumn))
                                {
                                    this.RaiseBeforeMappingDeletedEvent(ref beforeMappingEventArgs);
                                    if (!beforeMappingEventArgs.Cancelled)
                                    {
                                        this._controlMappingSurface.DeleteConnectors((string)connection.SourceConnectorObject); // results in the control's event handler being called, in which I call the "after" event
                                    }
                                    break;
                                }
                            }
                        }
                        if (!beforeMappingEventArgs.Cancelled)
                        {
                            this.RaiseBeforeMappingCreatedEvent(ref beforeMappingEventArgs);
                            if (!beforeMappingEventArgs.Cancelled)
                            {
                                this._controlMappingSurface.AddConnector(sourceDragColumn, destDragColumn); // results in the control's event handler being called, in which I call the "after" event
                            }
                        }
                    }
                }
            }
        }
        #endregion
        #endregion
        #endregion

        #region Event Raising
        public void SuspendEvents()
        {
            this._eventsSuspended = true;
        }

        public void ResumeEvents()
        {
            this._eventsSuspended = false;
        }

        protected virtual void RaiseBeforeMappingCreatedEvent(ref BeforeMappingEventArgs e)
        {
            try
            {
                if ((!this._eventsSuspended) && (this.BeforeMappingCreated != null))
                {
                    this.BeforeMappingCreated(this._controlMappingSurface, ref e);
                }
            }
            #region catch ...
            catch
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
                throw;
            }
            #endregion
        }

        protected virtual void RaiseBeforeMappingDeletedEvent(ref BeforeMappingEventArgs e)
        {
            try
            {
                if ((!this._eventsSuspended) && (this.BeforeMappingDeleted != null))
                {
                    this.BeforeMappingDeleted(this._controlMappingSurface, ref e);
                }
            }
            #region catch ...
            catch
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
                throw;
            }
            #endregion
        }

        protected virtual void RaiseAfterMappingCreatedEvent(AfterMappingEventArgs e)
        {
            try
            {
                if ((!this._eventsSuspended) && (this.AfterMappingCreated != null))
                {
                    this.AfterMappingCreated(this._controlMappingSurface, e);
                }
            }
            #region catch ...
            catch
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
                throw;
            }
            #endregion
        }

        protected virtual void RaiseAfterMappingDeletedEvent(AfterMappingEventArgs e)
        {
            try
            {
                if ((!this._eventsSuspended) && (this.AfterMappingDeleted != null))
                {
                    this.AfterMappingDeleted(this._controlMappingSurface, e);
                }
            }
            #region catch ...
            catch 
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
                throw;
            }
            #endregion
        }
        #endregion

        #region STRUCT: Mapping
        public struct Mapping
        {
            #region Private Variables
            private ColumnInformation _firstColumn;
            private ColumnInformation _secondColumn;
            #endregion

            #region Constructor
            public Mapping(ColumnInformation firstColumn, ColumnInformation secondColumn)
            {
                this._firstColumn = firstColumn;
                this._secondColumn = secondColumn;
            }
            #endregion

            #region Properties
            public string FirstColumnName
            {
                get { return this._firstColumn.Name; }
            }

            public string SecondColumnName
            {
                get { return this._secondColumn.Name; }
            }

            public ColumnInformation SecondColumnInformation
            {
                get { return this._secondColumn; }
            }
            #endregion

            public override string ToString()
            {
                return this._firstColumn.Name + " (" + this._firstColumn.LineageID.ToString() + ") <-> " + this._secondColumn.Name + " (" + this._secondColumn.LineageID.ToString() + ")";
            }
        }
        #endregion

        #region CLASS: MappingList
        public class MappingList : List<Mapping>
        {
            public void AddMapping(ColumnInformation firstColumn, ColumnInformation secondColumn)
            {
                Mapping newMapping = new Mapping(firstColumn, secondColumn);
                this.Add(newMapping);
            }

            public override string ToString()
            {
                return this.Count.ToString() + " items";
            }
        }
        #endregion
    }
    #endregion
}
